#include "EvolutionEngine.h"
#include "CandidateFactory.h"

EvolutionEngine::EvolutionEngine(int populationSize,int geneSize,Tournament *tour, FitnessEvaluator *fe, int epochSize, std::vector<Candidate*>  candidates)
{
    this->epochSize = epochSize;
    this->epochCount = 0;
    this->populationSize = populationSize;
    this->geneSize = geneSize;
    this->tour = tour;
    this->fe = fe;
    //cf = CandidateFactory();
    //this->cf = cf;
    //candidates = cf->generateInitialPopulation(populationSize,geneSize);
    this->candidates = candidates;
    this->finalValI = 0;
    this->finished = false;
}
EvolutionEngine::~EvolutionEngine() {
    delete this->tour;
    delete this->fe;
    //for(int i=0;i<(candidates.size());i++) {
    //        delete candidates.at(i);
    //}
    candidates.clear();
    //for(int i=0;i<(result.size());i++) {
    //        delete result.at(i);
    //}
    result.clear();
}

bool EvolutionEngine::evolve() {
    if(epochCount >= epochSize) {
        bulkEvolve();
        epochCount = 0;
    } else {
        epochCount++;
    }
    return finished;
}
void EvolutionEngine::finalizeEvolve() {
    this->finalValI = 0;
    result.clear();
    result = tour->getResult(candidates,populationSize);
    for(int i=0;i<(candidates.size());i++) {
            delete candidates.at(i);
    }
    candidates.clear();
    candidates = result;
    tour->clearResult();
    fe->evaluateFitness(candidates);
}
void EvolutionEngine::bulkEvolve() {
    this->finalValI = 0;
    result.clear();
    result = tour->select(candidates,populationSize);
    for(int i=0;i<(candidates.size());i++) {
            delete candidates.at(i);
    }
    candidates.clear();
    candidates = result;
    finished = fe->evaluateFitness(candidates);
}

Candidate* EvolutionEngine::migrateCandidate() {
    Candidate* winner;
    winner = candidates.at(0);
    int wIndex = 0;
    for(int i = 1;i<candidates.size();i++) {
        if(candidates.at(i)->getFitness() > winner->getFitness()) {
            winner = candidates.at(i);
            wIndex = i;
        }
    }
    candidates.erase(candidates.begin()+wIndex);
    return winner;
}
void EvolutionEngine::introduceImmigrant(Candidate* candidate) {
    if(candidate) {
        candidates.push_back(candidate);
    }
}
float EvolutionEngine::getFinalValue() {
    float finalValue = candidates.at(this->finalValI)->getFinalValue();
    this->finalValI = (this->finalValI+1)%candidates.size();
    return finalValue;
}
Candidate* EvolutionEngine::getWinner() {
    //fe->evaluateFitness(candidates);
    //return candidates.at(this->finalValI);
    return fe->returnWinner(candidates);
}

void EvolutionEngine::setTargetValue(float targetValue) {
    fe->setTargetValue(targetValue);
}
